import { App, ALL, Body, Inject, Controller, Post, Provide, Validate, Get, Query } from '@midwayjs/decorator'
import { Application, Context } from 'egg'

import { BaseController } from './base'
import { UserService } from '../service/user'
import { AuthService } from '../service/auth'
import { SmsService } from '../service/sms'
import { WechatService } from '../service/wechat'

import { LoginDTO, RegisterDTO, UserChangePasswordDTO, WechatLoginDTO } from '../dto/user'
import { CODE } from '../constants/code'

const _ = require('lodash')

@Provide()
@Controller('/api')
export class UserController extends BaseController {

  @App()
  app: Application

  @Inject()
  ctx: Context

  @Inject()
  userService: UserService

  @Inject()
  authService: AuthService

  @Inject()
  smsService: SmsService

  @Inject()
  wechatService: WechatService

  @Post('/login')
  @Validate()
  async login(@Body(ALL) login: LoginDTO) {
    const user = await this.userService.get({ username: login.username })

    if (!user || user.deleted_at) {
      return this.notOk(CODE.NOT_FOUND)
    }

    const isValid = await this.userService.validate(login.password, user.password)

    if (!isValid) {
      return this.notOk(CODE.USER_PASSWORD_INCORRECT)
    }

    const updatedUser = await this.userService.afterLogin(user, login.remember_me)

    return this.ok(updatedUser)
  }

  @Post('/wechat/login')
  @Validate()
  async wechatLogin(@Body(ALL) wechat: WechatLoginDTO) {
    const user = await this.wechatService.getUserInfo(wechat)

    if (!user || user.deleted_at) {
      return this.notOk(CODE.NOT_FOUND)
    }

    const safeUser = _.omit(user, ['password'])
    const updatedUser = await this.userService.afterLogin(safeUser, true)

    return this.ok(updatedUser)
  }

  @Post('/register')
  @Validate()
  async regiser(@Body(ALL) regiser: RegisterDTO) {
    const {
      username,
      // phone,
      // email,
      password,
      confirm_password
    } = regiser

    if (password !== confirm_password) {
      return this.notOk(CODE.USER_PASSWORDS_NOT_MATCH)
    }

    const existedUser = await this.userService.get({ username })

    if (existedUser) {
      return this.notOk(CODE.USER_EXISTED)
    }

    const addedUser = await this.userService.add({ username, password })

    return this.ok(
      _.omit(addedUser, ['password'])
    )
  }

  @Get('/info')
  async getUserInfo() {
    const { id } = this.ctx.state

    if (id) {
      const user = await this.userService.get({ id })

      if (user && user.deleted_at === null) {
        return this.ok(
          _.omit(user, ['password', 'openid'])
        )
      }

      return this.notOk(CODE.NOT_FOUND)
    }

    return this.notOk(CODE.MISSING_TOKEN)
  }

  @Post('/user/password')
  @Validate()
  async updateUserPwd(@Body(ALL) user: UserChangePasswordDTO) {
    const existedUser = await this.userService.get({ id: user.id })

    if (!existedUser) {
      return this.notOk(CODE.NOT_FOUND)
    }

    const { old_password, ...toUpdated } = user
    const isValid = await this.userService.validate(
      old_password,
      existedUser.password
    )

    if (!isValid) {
      return this.notOk(CODE.USER_PASSWORD_INCORRECT)
    }

    const updatedUser = await this.userService.update(toUpdated)

    return this.ok(
      _.omit(updatedUser, ['password'])
    )
  }

  @Get('/refresh_token')
  async refreshToken(@Query() token: string) {
    const returning = await this.authService.refreshToken(token)

    if (returning) {
      return this.ok(returning)
    }

    this.ctx.status = 401
    this.ctx.body = this.notOk(CODE.TOKEN_INVALID)
  }
}
